package com.joysuch.wwyt.repeatsubmit;

import com.joysuch.wwyt.common.constant.Constant;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.enums.Status;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.repeatsubmit.constant.RedisKey;
import com.joysuch.wwyt.repeatsubmit.exception.RepeatSubmitException;
import com.joysuch.wwyt.repeatsubmit.service.LockService;
import com.joysuch.wwyt.util.ReflectUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Aspect
@Component
public class NoRepeatSubmitAspect {

    private static final Logger logger = LoggerFactory.getLogger(NoRepeatSubmitAspect.class);

    @Autowired
    private LockService lockService;

    /**
     * 横切点
     */
    @Pointcut("@annotation(noRepeatSubmit)")
    public void repeatPoint(NoRepeatSubmit noRepeatSubmit) {
    }

    /**
     * 接收请求，并记录数据
     */
    @Around(value = "repeatPoint(noRepeatSubmit)", argNames = "joinPoint,noRepeatSubmit")
    public Object around(ProceedingJoinPoint joinPoint, NoRepeatSubmit noRepeatSubmit) {
        String key = buildKey(joinPoint, noRepeatSubmit);
        try {
            int seconds = noRepeatSubmit.seconds();
            if (!lockService.isLock(key, seconds)) {
                throw new RepeatSubmitException(Status.REQUESTS_TOO_FREQUENT);
            }
            return joinPoint.proceed();
        }catch (RepeatSubmitException e) {
            logger.error("重复提交", e);
            throw e;
        }catch (IllegalArgumentException ie) {
            logger.error("运行业务代码出错", ie);
            throw new IllegalArgumentException(ie.getMessage());
        } catch (Throwable throwable) {
            lockService.unLock(key);
            logger.error("运行业务代码出错", throwable);
            throw new RuntimeException(throwable.getMessage());
        }
    }

    private String buildKey(ProceedingJoinPoint joinPoint, NoRepeatSubmit noRepeatSubmit) {
        String key = String.format(RedisKey.NO_REPEAT_LOCK_PREFIX, Context.getCurrentTenantId(), Context.getCompanyCode());
        if (noRepeatSubmit.currentUser() && StringUtils.isNotBlank(key) && Context.getCurrentUserId() != null) {
            key = key + Context.getCurrentUserId().toString();
        }
        Object[] args = joinPoint.getArgs();
        int argIndex = noRepeatSubmit.argIndex();
        String suffix = null;
        if (args.length > 0) {
            suffix = builderSuffix(args[argIndex], noRepeatSubmit.paramNames());
        }else {
            suffix = builderSuffix(Constant.EMPTY_STR, noRepeatSubmit.paramNames());
        }
        if (StringUtils.isNotBlank(suffix)) {
            key = key + Constant.SPLIT_COLON + suffix;
        }
        return key;
    }

    private String builderSuffix(final Object object, final String paramNames) {
        List<String> paramNameList = Arrays.asList(paramNames.split(Constant.SPLIT_COMMA));
        StringBuilder suffix = new StringBuilder();
        if (!CollectionUtils.isEmpty(paramNameList)) {
            for (String paramName : paramNameList) {
                if (isCustomObject(object)) {
                    Map<String, Object> keyAndValue = ReflectUtil.getKeyAndValue(object);
                    Object valueObj = keyAndValue.get(paramName);
                    if (valueObj != null) {
                        if (suffix.length() > 0) {
                            suffix.append(Constant.SPLIT_HYPHEN);
                        }
                        suffix.append(valueObj);
                    }
                } else {
                    suffix.append(object);
                }
            }
        } else {
            if (!isCustomObject(object)) {
                suffix.append(object);
            } else {
                throw new IllegalArgumentException("NoRepeatSubmit parameter type is not supported, Please specify a parameter name");
            }
        }
        return suffix.toString();
    }


    public static boolean isCustomObject(Object obj) {
        return !(obj instanceof Number || obj instanceof String || obj instanceof Boolean);
    }
}
